using System;

namespace TrueCode.Core.Threading
{
	public static class Asynchronizer
	{
		public static IAsyncOperation<TVoid> InvokeAsync(this Action routine, AsyncOperationCompleted<TVoid> callback)
		{
			return InvokeAsync(routine, callback, null);
		}

		public static IAsyncOperation<TVoid> InvokeAsync(this Action routine, AsyncOperationCompleted<TVoid> callback, object state)
		{
			var operation = new AsyncOperation<TVoid>(callback, state);
			routine.BeginInvoke(
				OnRoutineForActionCompleted<TVoid>,
				new Tuple<Function<TVoid, IAsyncResult>, AsyncOperation<TVoid>>(
					ar =>
						{
							routine.EndInvoke(ar);
							return TVoid.Value;
						},
					operation));
			return operation;
		}


		public static IAsyncOperation<TVoid> InvokeAsync<T>(this Action<T> routine, T arg, AsyncOperationCompleted<TVoid> callback)
		{
			return InvokeAsync(routine, arg, callback, null);
		}
		public static IAsyncOperation<TVoid> InvokeAsync<T>(this Action<T> routine, T arg, AsyncOperationCompleted<TVoid> callback, object state)
		{
			var operation = new AsyncOperation<TVoid>(callback, state);
			routine.BeginInvoke(
				arg,
				OnRoutineForActionCompleted<TVoid>,
				new Tuple<Function<TVoid, IAsyncResult>, AsyncOperation<TVoid>>(
					ar =>
						{
							routine.EndInvoke(ar);
							return TVoid.Value;
						},
					operation));
			return operation;
		}

		private static void OnRoutineForActionCompleted<TResult>(IAsyncResult ar)
		{
			var tuple = (Tuple<Function<TResult, IAsyncResult>, AsyncOperation<TResult>>)ar.AsyncState;

			var endInvokeDelegate = tuple.ItemA;
			Exception caughtException = null;
			var result = default(TResult);
			try
			{
				result = endInvokeDelegate(ar);
			}
			catch(Exception exc)
			{
				caughtException = exc;
			}
			finally
			{
				tuple.ItemB.SetAsCompleted(result, caughtException);
			}
		}

//		public IAsyncOperation< TResult > Start< TResult, TArgs >( Function< TResult, TArgs, IAsyncOperation< TResult > > routine, TArgs arg, AsyncOperationCompleted< TResult > callback, Object state )
//		{
//			var operation = new AsyncOperation< TResult >( callback, state );
//
//			routine.BeginInvoke( arg, operation,  ar =>
//			{
//				Exception caughtException = null;
//				TResult r = default( TResult );
//				try
//				{
//					r = routine.EndInvoke( ar );
//				}
//				catch( Exception exception )
//				{
//					caughtException = exception;
//				}
//				finally
//				{
//					operation.SetAsCompleted(r, caughtException, ar.CompletedSynchronously );
//				}
//			}, null );
//
//			return operation;
//		}
//
//		private void OnRoutineCompleted<TResult>(IAsyncResult ar)
//		{
//			Exception caughtException = null;
//			TResult r = default(TResult);
//			try
//			{
//				r = routine.EndInvoke(result);
//			}
//			catch(Exception exception)
//			{
//				caughtException = exception;
//			}
//			finally
//			{
//				operation.SetAsCompleted(r, caughtException, result.CompletedSynchronously);
//			}
//		}
	}
}
